package com.atguigu.gmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.pms.entity.BrandEntity;
import com.atguigu.gmall.pms.entity.CategoryEntity;
import com.atguigu.gmall.search.pojo.Goods;
import com.atguigu.gmall.search.pojo.SearchParamVO;
import com.atguigu.gmall.search.pojo.SearchResponseAttrVO;
import com.atguigu.gmall.search.pojo.SearchResponseVO;
import com.atguigu.gmall.search.service.SearchService;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient highLevelClient;
    @Override
    public SearchResponseVO search(SearchParamVO paramVO) {

        try {
            SearchRequest searchRequest = new SearchRequest();
            //设置索引库
            searchRequest.indices("goods");
            //设置搜索条件
            searchRequest.source(this.buILdDSL(paramVO));
            SearchResponse response = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //解析结果集并返回
            SearchResponseVO searchResponseVO = this.parsedResult(response);
            //设置分数数据
            searchResponseVO.setPageNum(paramVO.getPageNum());
            searchResponseVO.setPageSize(paramVO.getPageSize());
            return searchResponseVO;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据 参数模型构建DSL语句
     * @param paramVO
     * @return
     */
    private SearchSourceBuilder buILdDSL(SearchParamVO paramVO){
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        String keyword = paramVO.getKeyword();
        if (StringUtils.isBlank(keyword)){
            //TODO:打广告
            throw new RuntimeException("请输入搜索关键字！");
        }
        // 1 获取查询及过滤条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        // 1.1 获取查询关键字 匹配条件

        sourceBuilder.query(boolQueryBuilder.must(QueryBuilders
                .matchQuery("title",keyword).operator(Operator.AND)));
        // 1.2 根据品牌id过滤
        List<Long> brandId = paramVO.getBrandId();
        if (!CollectionUtils.isEmpty(brandId)){
        boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", brandId));
        }

        // 1.3 根据分类id过滤
        List<Long> categoryId = paramVO.getCategoryId();
        if (!CollectionUtils.isEmpty(categoryId)){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("categoryId",categoryId));
        }
        // 1.4 根据价格范围过滤
        Double priceForm = paramVO.getPriceForm();
        Double priceTo = paramVO.getPriceTo();
        //只要有一个不为空就可以了
        if (priceForm != null || priceTo !=null){
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            boolQueryBuilder.filter(rangeQuery);
            if (priceForm !=null){
                rangeQuery.gte(priceForm);
            }
            if (priceTo!=null){
                rangeQuery.lte(priceTo);
            }
        }

        // 1.5 是否有货
        Boolean store = paramVO.getStore();
        //只要有这个参数就代表有货 为了测试 写无货 false
        if (store != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("store",store));
        }

        // 1.6 根据规格参数过滤 嵌套属性 4:8G-12G
        List<String> props = paramVO.getProps();
        if (!CollectionUtils.isEmpty(props)){
            props.forEach(prop->{
                //以冒号分割 第一个为attrId
                String[] attrStr = StringUtils.split(prop, ":"); // [4,8G-12G]
                if (attrStr!=null || attrStr.length ==2 &StringUtils.isNumeric(attrStr[0])){ //数组第一个元素必须是数字

                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                    //词条查询
                    boolQuery.must(QueryBuilders.termQuery("searchAttrs.attrId",attrStr[0]));
                    //多词条查询
                    String[] valueStr = StringUtils.split(attrStr[1], "-");
                    boolQuery.must(QueryBuilders.termsQuery("searchAttrs.attrValue",valueStr));
                    boolQueryBuilder.filter(QueryBuilders.nestedQuery("searchAttrs",boolQuery, ScoreMode.None));
                }
            });
        }
        // 2 获取排序条件 默认得分排序  1 价格降序  2  价格升序 3 销量 4 新品 创建时间
        Integer sort = paramVO.getSort();
        if (sort != null){
            switch (sort){
                case 1: sourceBuilder.sort("price", SortOrder.DESC);break;
                case 2: sourceBuilder.sort("price", SortOrder.ASC);break;
                case 3: sourceBuilder.sort("sales", SortOrder.DESC);break;
                case 4: sourceBuilder.sort("createTime", SortOrder.ASC);break;
                default:sourceBuilder.sort("_source",SortOrder.DESC);
            }
        }

        // 3 获取分类条件
        Integer pageNum = paramVO.getPageNum();
        Integer pageSize = paramVO.getPageSize();
        sourceBuilder.from((pageNum -1 )* pageSize);
        sourceBuilder.size(pageSize);
        // 4 获取高量条件 不需要参数 添加高亮字段即可
        sourceBuilder.highlighter(new HighlightBuilder()
                .field("title").preTags("<font style='color:red;'>").postTags("</font>"));
        // 5 获取聚合条件
        // 5.1根据品牌聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                .subAggregation(AggregationBuilders.terms("logoAgg").field("logo")));
        // 5.2 根据分类聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId")
                .subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));
        // 5.3 根据规参数聚合 嵌套聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("attrAgg","searchAttrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));

        // 6设置结果集过滤
        sourceBuilder.fetchSource(new String[]{"skuId","title","subtitle","price","defaultImage"},null);
        System.out.println(sourceBuilder);
        return sourceBuilder;
    }

    /**
     * 解析结果集
     * @param response
     * @return
     */
    private SearchResponseVO parsedResult(SearchResponse response){
        SearchResponseVO searchResponseVO = new SearchResponseVO();

        //解析普通结果集
        SearchHits hits = response.getHits();
        //设置总记录条数
        searchResponseVO.setTotal(hits.getTotalHits().value);
        SearchHit[] hitsHits = hits.getHits();
        //将 hitsHits 转换为goodsList集合
        if (hitsHits ==null || hitsHits.length == 0){
            throw new RuntimeException("没有搜索到数据！");
        }
        searchResponseVO.setGoodsList(Arrays.stream(hitsHits).map(hitsHit->{
            //获取_source json字符串
            String json = hitsHit.getSourceAsString();
            //反序列化为goods对象
            Goods goods = JSON.parseObject(json, Goods.class);
            //获取高量结果集并覆盖
            Map<String, HighlightField> highlightFields = hitsHit.getHighlightFields();
            //获取第一个字段
            HighlightField highlightField = highlightFields.get("title");
            Text[] fragments = highlightField.fragments();
                goods.setTitle(fragments[0].string());
            return goods;
        }).collect(Collectors.toList()));
        //分页数据从参数模型获取
        //解析聚合结果集

        Aggregations aggregations = response.getAggregations();
        //获取品牌id的聚合结果集
        ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
        List<? extends Terms.Bucket> brandIdBuckets = brandIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(brandIdBuckets)){
            //将品牌id桶集合转换为实体类对象集合 并设置
            searchResponseVO.setBrands(brandIdBuckets.stream().map(brandIdBucket->{
                BrandEntity brandEntity = new BrandEntity();
                brandEntity.setId(((Terms.Bucket) brandIdBucket).getKeyAsNumber().longValue());
                //获取子聚合 获取品牌名称
                Aggregations subAggs = ((Terms.Bucket) brandIdBucket).getAggregations();
                ParsedStringTerms brandNameAgg = subAggs.get("brandNameAgg");
                List<? extends Terms.Bucket> brandNameAggBuckets = brandNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(brandNameAggBuckets)){
                    brandEntity.setName(brandNameAggBuckets.get(0).getKeyAsString());
                }

                //获取品牌logo
                ParsedStringTerms logoAgg = subAggs.get("logoAgg");
                List<? extends Terms.Bucket> logoAggBuckets = logoAgg.getBuckets();
                if (!CollectionUtils.isEmpty(logoAggBuckets)){
                    brandEntity.setLogo(logoAggBuckets.get(0).getKeyAsString());
                }
                return brandEntity;
            }).collect(Collectors.toList()));
        }

        //获取分类的聚合结果集 并解析
        ParsedLongTerms categoryIdAgg = aggregations.get("categoryIdAgg");
        List<? extends Terms.Bucket> categoryIdAggBuckets = categoryIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(categoryIdAggBuckets)){
            //将分类id的桶对象转换为实体类对象
            searchResponseVO.setCategories(categoryIdAggBuckets.stream().map(categoryIdAggBucket->{
                CategoryEntity categoryEntity = new CategoryEntity();
                categoryEntity.setId(((Terms.Bucket) categoryIdAggBucket).getKeyAsNumber().longValue());

                //获取子聚合
                Aggregations subAggs = ((Terms.Bucket) categoryIdAggBucket).getAggregations();
                //根据子聚合获取分类名称
                ParsedStringTerms categoryNameAgg = subAggs.get("categoryNameAgg");
                List<? extends Terms.Bucket> categoryNameAggBuckets = categoryNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(categoryNameAggBuckets)){
                    categoryEntity.setName(categoryNameAggBuckets.get(0).getKeyAsString());
                }
                return categoryEntity;
            }).collect(Collectors.toList()));
        }

        //获取规格参数的聚合结果集
        ParsedNested attrAgg = aggregations.get("attrAgg"); //嵌套聚合 本身没有意义
        //获取子聚合中的规格参数id的聚合
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<? extends Terms.Bucket> attrIdAggBuckets = attrIdAgg.getBuckets();
        if (!CollectionUtils.isEmpty(attrIdAggBuckets)){
            //将规格参数id桶对象转换为 filters 对象
            searchResponseVO.setFilters(attrIdAggBuckets.stream().map(attrIdAggBucket->{
                SearchResponseAttrVO searchResponseAttrVO = new SearchResponseAttrVO();
                //当前桶的key就是attrId
                searchResponseAttrVO.setAttrId(((Terms.Bucket) attrIdAggBucket).getKeyAsNumber().longValue());
                //获取子聚合
                Aggregations subAgg = ((Terms.Bucket) attrIdAggBucket).getAggregations();
                //通过子集合获取规格参数名称的聚合
                ParsedStringTerms attrNameAgg = subAgg.get("attrNameAgg");
                List<? extends Terms.Bucket> attrNameAggBuckets = attrNameAgg.getBuckets();
                if (!CollectionUtils.isEmpty(attrNameAggBuckets)){
                    searchResponseAttrVO.setAttrName(attrNameAggBuckets.get(0).getKeyAsString());
                }

                //通过子聚合获取规格参数值的聚合
                ParsedStringTerms attrValueAgg = subAgg.get("attrValueAgg");
                List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
                if (!CollectionUtils.isEmpty(attrValueAggBuckets)){
                    //桶中key的集合就是规格参数值的集合
                    searchResponseAttrVO.setAttrValue(attrValueAggBuckets.stream()
                            .map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
                }
                return searchResponseAttrVO;
            }).collect(Collectors.toList()));
        }
        return searchResponseVO;
    }
}
